/**********************************************/
/*** Programacao Concorrentes               ***/
/*** Trabalho 1                             ***/
/*** Grupo 03 Turma A                       ***/
/*** Brenno Candido                 7696500 ***/
/*** Jose Victor Uliana Martins     7656620 ***/
/*** Maria Fernanda Garbim Avelino  7277562	***/
/**********************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define FILTER_SIZE 5 // dimesao do filtro
#define FILTER_SIZE_SQUARED FILTER_SIZE*FILTER_SIZE

/** variavel de 1 byte que pode conter valores de 0-255 **/
typedef unsigned char uchar;

/*
 * Estrutura que contem os dados de uma image
 * no formato PPM
 * OBS: o header da imagem NAO deve ter COMENTARIO
 */
typedef struct {
	char identifier[3];
	int width, height, colors_range;
	uchar *r, *g, *b;
} Image;

/*
 * Funcao que realiza a leitura de uma imagem em
 * PPM que NAO posssi COMENTARIO
 * Argumentos:
 * 	f_in (input file): descritor do arquivo da imagem PPM a ser lida
 * 	in: estrutura de dados a ser amazenados a imagem lida
 * 	em f_in
 */ 
void readImage(FILE* f_in, Image* in) {
	int i, j;
	
	// Le dos dados do header da image PPM
	fscanf(f_in, " %s %d %d %d", in->identifier,  &in->width, &in->height, &in->colors_range);
	
	// Aloca vetores de cores no padrao RGB
	in->r = (uchar*)malloc(in->width*in->height*sizeof(uchar));
	in->g = (uchar*)malloc(in->width*in->height*sizeof(uchar));
	in->b = (uchar*)malloc(in->width*in->height*sizeof(uchar));
	
	// Le os dados dos bytes de cores RGB
	for(i=0; i<in->height; i++) {
		for(j=0; j<in->width; j++) {
			fscanf(f_in, " %hhu", &in->r[in->width*i+j]);
			fscanf(f_in, " %hhu", &in->g[in->width*i+j]);
			fscanf(f_in, " %hhu", &in->b[in->width*i+j]);
		}
	}
}

/*
 * Funcao que realiza a escrita de uma imagem em PPM
 * Argumentos:
 * 	f_out (output file): descritor de arquivo da imagem PPM a ser escrito
 * 	out: estrutura de dados com os dados da imagem a ser escrita
 * 	em f_out
 */
void writeImage(FILE* f_out, Image *out) {
	int i, j;
	
	// Escreve os dados do header da imagem PPM
	fprintf(f_out, "%s\n%d %d\n%d\n", out->identifier, out->width,out->height, out->colors_range);
	
	// Escreve os dados dos bytes de cores RGB
	for(i=0; i<out->height; i++) {
		for(j=0; j<out->width; j++) {
			fprintf(f_out, "%hhu ", out->r[out->width*i+j]);
			fprintf(f_out, "%hhu ", out->g[out->width*i+j]);
			fprintf(f_out, "%hhu ", out->b[out->width*i+j]);
		}
		fprintf(f_out, "\n");
	}
}

/*
 * Desaloca memoria usada pela estrutura da imagem PPM
 * Argumentos:
 * 	img (image): ponteiro da imagem a ser desalocada
 */
void clear(Image *img) {
	img->identifier[0] = '\0';
	img->width = 0;
	img->height = 0;
	img->colors_range = 0;
	free(img->r);
	free(img->g);
	free(img->b);
	img->r = NULL;
	img->g = NULL;
	img->b = NULL;
}

/*
 * Copia o cabecalho da imagem b para a imagem, a = b
 * Argumentos:
 * 	a (imagem): imagem alvo
 * 	b (imagem): imagem fonte
 */
void cpyHeader(Image *a, Image *b) {
	strcpy(a->identifier, b->identifier);
	a->width = b->width;
	a->height = b->height;
	a->colors_range = b->colors_range;
	
	// Aloca vetores de cores no padrao RGB
	a->r = (uchar*)malloc(b->width*b->height*sizeof(uchar));
	a->g = (uchar*)malloc(b->width*b->height*sizeof(uchar));
	a->b = (uchar*)malloc(b->width*b->height*sizeof(uchar));
}

/*
 * Passa a mascara smooth em 1 pixel
 * Argumentos:
 * 	px (pixels): vetor de pixels
 * 	i: indice de linha da posicao do pixel
 * 	j: indice de coluna do pixel
 * 	width (largura): largura da imagem para pode ser realizado
 * 	o acesso na posicao do pixel (i,j) correto do vetor
 * Retorno:
 * 	Novo valor do pixel (i, j) da imagem
 */
uchar smooth(uchar* px, int i, int j, int width){
	int aux_i, aux_j, sum;
	int filter_radius = FILTER_SIZE/2; // Raio do filtro
	
	/*
	 * Aplica a mascara de raio filter_radius para o pixel (i, j)
	 */
	sum = 0; 
	for(aux_i = i-filter_radius; aux_i<=i+filter_radius; aux_i++) {
		for(aux_j = j-filter_radius; aux_j<=j+filter_radius; aux_j++) {
			if(aux_i >= 0 && aux_j >= 0) {
				sum += px[aux_i*width+aux_j];
			}
			else
				sum += px[i*width+j];
		}
	}
	return sum/(FILTER_SIZE*FILTER_SIZE);
}

/*
 * Funcao que passa o a mascara smooth em todos os
 * pixels da imagem passada no primeiro parametro e
 * salva a na imagem passada no segundo parametro.
 * Argumentos:
 * 	in: image fonte
 * 	out: image alvo
 * OBS: passa o filtro na imagem in e salva na image imagem out
 */
void smoothFilter(Image *in, Image *out) {
	int i, j;
	
	for(i=0; i<in->height; i++) {
		for(j=0; j<in->width; j++) {
			out->r[i*in->width+j] = smooth(in->r, i, j, in->width);
			out->g[i*in->width+j] = smooth(in->g, i, j, in->width);
			out->b[i*in->width+j] = smooth(in->b, i, j, in->width);
		}
	}
}

int main(int argc, char* argv[]) {
	Image in, out;
	FILE* f_in, *f_out;
	
	// Abre arquivo passado como primeiro argumento de execucao (argv[1])
	f_in = fopen(argv[1], "r");
	if(!f_in)
		fprintf(stderr, "Can not read a file \"%s\". Please, check that the file exists\n", argv[1]);
	
	// Le imagem do arquivo
	readImage(f_in, &in);
	fclose(f_in);
	
	// Copia o header da imagem lida para a novo imagem que sera gerada como saida
	cpyHeader(&out, &in);
	
	/*
	 * Para obter uma medida de tempo mais acurada para realizar
	 * a analise do algoritmo sequencial do smoothFilter(), para
	 * isso utilizamos as variaveis do tipo clock_t
	 * t1: tempo inicial
	 * t2: tempo final
	 * dt: tempo levado para executar a funcao smoothFilter()
	 */
	clock_t t1, t2, dt;
	t1 = clock(); // tempo inicial
	
	smoothFilter(&in, &out);
	
	t2 = clock(); // tempo final
	printf("time = %.3lf\n", (double)(t2-t1)/CLOCKS_PER_SEC);
	
	// Abre arquivo para escrever a image out
	f_out = fopen("out.ppm", "w");
	if(!f_out)
		fprintf(stderr, "Can not write a file \"out.ppm\"\n");
	
	// Escreve a image out em f_out
	writeImage(f_out, &out);
	fclose(f_out);
	
	// Limpa memoria
	clear(&in);
	clear(&out);
	return 0;
}
